package com.glsc.ngateway.wordconver.service;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.wordconver.constant.Constant;
import com.glsc.ngateway.wordconver.exception.WordConverException;
import com.glsc.ngateway.wordconver.utils.PdfUtil;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class WordToPdfService {

    //word 转换为 pdf 的格式宏，值为 17
    private static final int WORD_FORMAT_PDF = 17;
    //word office应用。 Word.Application.   WPS: KWPS.Application
    private static final String MS_OFFICE_APPLICATION = "Word.Application";
    private static Logger logger = LoggerFactory.getLogger(WordToPdfService.class);

    @Value("${exes.chromedriver.url}")
    private String chromeDriverUrl;

    @Value("${outfile.url}")
    private String outfileUrl;

    @Value("${product-center.url}")
    private String productCenterUrl;

    //依赖 jacob.dll + jacob.jar
    public String toPdf(String src) {
        logger.info("开始转换文件成pdf:" + src);
        String substring = src.substring(0, src.lastIndexOf("."));
        String tar = substring + ".pdf";
        ActiveXComponent app = null;
        Dispatch doc = null;
        try {
            app = new ActiveXComponent(MS_OFFICE_APPLICATION);
            app.setProperty("Visible", new Variant(false));
            app.setProperty("AutomationSecurity", new Variant(3));
            Dispatch docs = app.getProperty("Documents").toDispatch();
            doc = Dispatch.call(docs, "Open",
                            src, Boolean.FALSE, Boolean.TRUE)
                    .toDispatch();
            Dispatch.call(doc, "SaveAs",
                    tar, WORD_FORMAT_PDF);
        } catch (Exception e) {
            logger.error("开始转换文件成pdf发生异常:" + e.getLocalizedMessage());
            logger.error("异常", e);
        } finally {
            if (doc != null) {
                try {
                    Dispatch.call(doc, "Close", Boolean.FALSE);
                } catch (Exception e) {
                    logger.error("开始转换文件成pdf发生异常:" + e.getLocalizedMessage());
                    logger.error("异常", e);
                }
            }
            if (app != null) {
                try {
                    app.invoke("Quit", new Variant(false));
                    app.safeRelease();
                } catch (Exception e) {
                    logger.error("开始转换文件成pdf发生异常:" + e.getLocalizedMessage());
                    logger.error("异常", e);
                }
            }

        }
        logger.info("成功转换文件成pdf:" + tar);
        return tar;
    }

    public byte[] converToPdf(MultipartFile file) {
        byte[] byt;
        try {
            String src = saveMultipartFile(file);
            String resFile = toPdf(src);
            File resfile = new File(resFile);
            InputStream input = new FileInputStream(resfile);
            byt = new byte[input.available()];
            input.read(byt);
        } catch (Exception e) {
            logger.error("转换成pdf时,发生异常" + e.getLocalizedMessage());
            throw WordConverException.error("转换失败," + e.getMessage());
        }
        logger.info("成功转换文件成pdf,返回字节数组:" + byt.length);
        return byt;
    }

    private String saveMultipartFile(MultipartFile file) {
        String targetDirPath = Constant.BASE_DIR;
        File toFile = null;
        if (file.isEmpty()) {
            logger.error("保存文件至本地时发生异常：文件为空");
            throw WordConverException.error("保存文件至本地时发生异常：文件为空");
        } else {
            /*获取文件原名称*/
            String originalFilename = file.getOriginalFilename();
            /*获取文件格式*/
            String fileFormat = originalFilename.substring(originalFilename.lastIndexOf("."));

            String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
            toFile = new File(targetDirPath + File.separator + uuid + fileFormat);

            String absolutePath = null;
            try {
                absolutePath = toFile.getCanonicalPath();
                /*判断路径中的文件夹是否存在，如果不存在，先创建文件夹*/
                String dirPath = absolutePath.substring(0, absolutePath.lastIndexOf(File.separator));
                File dir = new File(dirPath);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                InputStream ins = file.getInputStream();
                inputStreamToFile(ins, toFile);
                ins.close();
            } catch (IOException e) {
                logger.error("保存文件至本地时发生异常：" + e.getLocalizedMessage());
                logger.error("异常", e);
            }
            logger.info("保存文件至本地：" + absolutePath);
            return absolutePath;
        }

    }

    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            logger.error("Inputstream写入文件时异常:" + e.getLocalizedMessage());
            logger.error("异常", e);
        }
    }

    @Nullable
    public byte[] getPdfFromOaUrl(String srcPath, String sessionId) throws IOException {
        AtomicReference<File> file = new AtomicReference<>();
//        delayThreadPool.schedule(() -> {
        try {
            logger.info("开始sessionId:" + sessionId);
            file.set(PdfUtil.getPdfFromOaUrl(srcPath, sessionId, chromeDriverUrl, outfileUrl));
            logger.info("结束sessionId:" + sessionId + ", file:" + file.get().length());
        } catch (Exception e) {
            logger.error("异常", e);
            logger.info("异常sessionId:" + sessionId + ", exception:" + e);
        }
//        }, 1000, TimeUnit.MILLISECONDS);

        if (Objects.nonNull(file.get())) {
            FileInputStream fis = new FileInputStream(file.get());
            byte[] bytes = new byte[(int) file.get().length()];
            fis.read(bytes);
            fis.close();
            return bytes;
        } else {
            return null;
        }
    }


    @Autowired
    private RestTemplate restTemplate;

    public byte[] signPdfWithAccount(MultipartFile pdf, MultipartFile gif) throws IOException {
        return signPdfWithAccount(pdf, gif, "zzp");
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class ProductCenterResponseEntity<T> {
        public static final Integer SUCCESS = 10000;
        Integer code;
        String message;
        T data;

        public boolean isSuccess() {
            return Objects.nonNull(this.code) && this.code.equals(SUCCESS);
        }
    }

    public byte[] signPdfWithAccount(MultipartFile pdf, MultipartFile gif, String account) throws IOException {
        //获取token：http://10.197.49.41/api/user/genToken
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8"));
        HttpEntity<String> formEntity = new HttpEntity<>(JSONObject.toJSONString(Map.of("account", account)), headers);
        ResponseEntity<ProductCenterResponseEntity> tokenResp = restTemplate.postForEntity(productCenterUrl + "/api/user/genToken", formEntity, ProductCenterResponseEntity.class);
        if (!tokenResp.getStatusCode().is2xxSuccessful()) {
            logger.error("产品中心-获取token异常：{}, {}", account, tokenResp);
            return null;
        }
        ProductCenterResponseEntity<String> tokenBody = tokenResp.getBody();
        if (Objects.isNull(tokenBody) || !tokenBody.isSuccess()) {
            logger.error("产品中心-获取token失败：{}, {}", account, tokenBody);
            return null;
        }
        String token = tokenBody.getData();
        return signPdfWithToken(pdf, gif, token);
    }

    public byte[] signPdfWithToken(MultipartFile pdf, MultipartFile gif, String token) throws IOException {
        //使用token签字：http://10.197.49.41/api/sign/signPdf
        HttpHeaders headers = new HttpHeaders();
        headers.set("token", token);
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        //提交参数设置
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("pdf", new ByteArrayResource(pdf.getBytes()) {
            @Override
            public String getFilename() {
                return pdf.getOriginalFilename();
            }
        });
        param.add("gif", new ByteArrayResource(gif.getBytes()) {
            @Override
            public String getFilename() {
                return gif.getOriginalFilename();
            }
        });
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(param, headers);
        ResponseEntity<ProductCenterResponseEntity> resp = restTemplate.postForEntity(productCenterUrl + "/api/sign/signPdf", formEntity, ProductCenterResponseEntity.class);
        if (!resp.getStatusCode().is2xxSuccessful()) {
            logger.error("产品中心-签字异常：{}", resp);
            return null;
        }
        ProductCenterResponseEntity<String> tokenBody = resp.getBody();
        if (Objects.isNull(tokenBody) || !tokenBody.isSuccess()) {
            logger.error("产品中心-签字失败：{}", tokenBody);
            return null;
        }
        return Base64Decoder.decode(tokenBody.getData());
    }
}
